
 
/*  search.c
    used with tm40.c.
    Stephen D. Anderson --- May 17, 1995

    Function searches for all field instruments present on network.
    Asks user to select one.  Does the following:

    Builds a generic transmit message of HART command 0 that can be sent
    to each polling address by modifying only the address and the
    checksum.  The size of the preamble in these searches is set
    according to the desired execution speed in header file "send_rec.h".

    HART command 0 is sent up to 3 times for each of 16 polling addresses.
    If a device is found, there are no further tries at that address.

    During the trials, various error conditions are checked.  If a response
    is deemed valid, it is saved in matrix "cmd0_receive[trial_address][byte],
    an array of polling addresses, "addrs[]", is set to indicate that a
    device exists at the address, and the number of received bytes of the
    response is saved in an array "cmd0_rec_bytes[trial_address].

    The information in every valid response is saved so that another
    field instrument can later be selected without having to do another
    complete search of the polling addresses.

    Once the search is done, the user is asked to select which polling
    address to use.  The procedure is the same as that described for
    module select.c.


    ON ENTRY:

    ON EXIT:

    REVISION LOG:

    Version 4.3:
    1.  Changed to mask out burst bit when checking whether received
        address matches transmitted address.


*/

#include "tm40.h"
#include "send_rec.h"

#if (SPEED == TURBO)
    #define PREAMBLES 5
#else
    #define PREAMBLES 20
#endif

byte search()
{
    extern int cmd0_rec_bytes[];
    extern byte cmd0_receive[][50];
    extern byte send[], receive[], receive_err[];
    extern byte status1, status2;
    extern byte polling_addr, addr[];
    extern byte found;
    extern byte status_wind;
    extern byte addrs[];

    extern int send_rec(byte);
    extern byte Checksum(byte *, byte);
    extern byte Checkpar(byte *, byte);

    int i1;
    byte i,j,k;
    byte n_tries;
    byte cs_start;
    byte *r,*s,*re;
    byte start_delim[3] = {0xff, 0xff, 6};
    byte trial_address;
    byte byte_count;
    int rec_bytes;
    byte *a;
    byte ss[5];

    Wopen(3,18,16,62,2,WHITE|_BLUE,WHITE|_BLUE);
    WputCen("\n\nSearching For Field Instruments ... ");

    /*  build the xmit message. */
    for (i=0; i<PREAMBLES; ++i) send[i]=0xff;    /* preamble. */
    send[i++]=2;    /*  start delimiter. */
    s=&send[i];         /* point to start of message. */
    send[i++] = 0;      /* space for address byte. */
    send[i++] = 0;      /* command. */
    send[i++] = 0;      /* byte count. */

    /*  try all addresses.  */
    Wopen(10,25,13,55,1,WHITE|_GREEN,WHITE|_GREEN);

    /*  do all 16 polling addresses.  */
    for (trial_address=0; trial_address<16; ++trial_address)
    {
        addrs[trial_address] = 0;   /*  assume no device.  */
        /*  retry each address 3 times.  */
        for (n_tries=1; n_tries<=3; ++n_tries)
        {
            Wclear();
            Wputf("\n  Trying Polling Address %d ",trial_address);
            *s = 0x80 | trial_address;  /*  generate the short address. */
            /*  generate xmit checksum.  */
            send[i] = Checksum((send+PREAMBLES), i-PREAMBLES);
            rec_bytes = send_rec(i+1);  /*  do comm.  */
            if ((rec_bytes & 0xff) == 1) continue;
            if ((rec_bytes & 0xff) == 3) continue;

            k = rec_bytes/256;

            /*  find start delimiter.  */
            r = receive;
            for (j=1; j <= k; ++j)
            {
                if ((strncmp(r++, start_delim, 3)) == 0) break;
            }

            /* go to start delim and check numb bytes left. */
            j += 2; ++r;
            if (j > k)
            {
                WpopUp(CNT_CNT,10,10,20,50,3,WHITE|_RED,WHITE|_RED);
                WputCen("\n\nField Instrument Response Error");
                Wputf("\n    at address %d", trial_address);
                WputCen("\n\nNo or bad start delimiter.");
                WputCen("\nGoing to next trial.");
                delay(1000); Wclose(); continue;
            }

            cs_start = j;   /* mark position. */

            ++j; ++r;       /* advance to address. */

            /* are there at least 2 characters left? */
            if ((k-j+1) < 2) 
            {
                WpopUp(CNT_CNT,10,10,20,50,3,WHITE|_RED,WHITE|_RED);
                WputCen("\n\nField Instrument Response Error");
                Wputf("\n    at address %d", trial_address);
                WputCen("\n\nReceived msg too short.");
                WputCen("\nGoing to next trial.");
                delay(1000); Wclose(); continue;
            }

            /*  does address with burst bit masked out match?  */
            *s = 0x80 | trial_address;   /*  generate the short address. */
            if ( (*r & 0xbf) != *s)
            {
                WpopUp(CNT_CNT,10,10,20,50,3,WHITE|_RED,WHITE|_RED);
                WputCen("\n\nField Instrument Response Error");
                Wputf("\n    at address %d", trial_address);
                WputCen("\n\nEchoed address doesn't match.");
                WputCen("\nGoing to next trial.");
                delay(1000); Wclose(); continue;
            }

            ++j; ++r;   /*  advance to command.  */

            /*  does command match?  */
            if ( *r != *(s+1) ) 
            {
                WpopUp(CNT_CNT,10,10,20,50,3,WHITE|_RED,WHITE|_RED);
                WputCen("\n\nField Instrument Response Error");
                Wputf("\n    at address %d", trial_address);
                WputCen("\n\nEchoed command doesn't match.");
                WputCen("\nGoing to next trial.");
                delay(1000); Wclose(); continue;
            }

            ++j; ++r;   /*  advance to byte count.  */

            if (j > k)
            {
                WpopUp(CNT_CNT,10,10,20,50,3,WHITE|_RED,WHITE|_RED);
                WputCen("\n\nField Instrument Response Error");
                Wputf("\n    at address %d", trial_address);
                WputCen("\n\nReceived msg too short.");
                WputCen("\nGoing to next trial.");
                delay(1000); Wclose(); continue;
            }

            byte_count = *r;    /*  save byte count. */

            ++j;  ++r;  /*  skip to status. */
            status1 = *r++; ++j;     /*  save it.  */
            status2 = *r++; ++j;

            /*  field instrum. reporting communication error? */
            if ((status1 & 0x80) != 0)
            {
                WpopUp(CNT_CNT,10,10,20,50,3,WHITE|_RED,WHITE|_RED);
                WputCen("\n\nField Instrument Reports");
                WputCen("\nCommunication Error");
                Wputf("\n    at address %d", trial_address);
                WputCen("\nGoing to next trial.");
                delay(1000); Wclose(); continue;
            }

            /*  field instrum reporting command error? */
            if ((status1 == 6) || (status1 == 16)) 
            {
                WpopUp(CNT_CNT,10,10,20,50,3,WHITE|_RED,WHITE|_RED);
                WputCen("\n\nField Instrument Reports");
                WputCen("\nCommand Error");
                Wputf("\n    at address %d", trial_address);
                WputCen("\nGoing to next trial.");
                delay(1000); Wclose(); continue;
            }

            /* byte count either 12 or 14? */
            if ( (byte_count != 14) && (byte_count != 12))
            {
                WpopUp(CNT_CNT,10,10,20,50,3,WHITE|_RED,WHITE|_RED);
                WputCen("\n\nField Instrument Response Error");
                Wputf("\n    at address %d", trial_address);
                WputCen("\n\nByte count wrong.");
                WputCen("\nGoing to next trial.");
                delay(1000); Wclose(); continue;
            }

            /*  now pointing at 1st byte of receive data. */
            a = addr;   /*  set pointer. */
            if (*r != 254)
            {
                WpopUp(CNT_CNT,10,10,20,50,3,WHITE|_RED,WHITE|_RED);
                WputCen("\n\nField Instrument Response Error");
                Wputf("\n    at address %d", trial_address);
                WputCen("\n\nContinuation byte (254) missing.");
                WputCen("\nGoing to next trial.");
                delay(1000); Wclose(); continue;
            }

            /*  finish the message. */

            r += 3;         /*  point to numb preambles. */
            *a++ = *r++;    /*  get numb preambles. */
            r -= 3;         /*  point to MFR ID. */
            *a++ = *r++;    /*  get MFR ID (1st byte of addr). */
            *a++ = *r++;    /*  get MFR DEVICE TYPE (2nd byte). */

            r += 6;     /*  point to DEVICE ID MSB (3rd addr byte).  */
            *a++ = *r++;    /*  get 3 bytes */
            *a++ = *r++;
            *a++ = *r++;

            r -= 6;         /*  back up to software rev of dev. */
            *a++ = *r++;    /*  get software rev of device. */
            *a++ = *r++;    /*  get hardware rev of device. */
            *a = *r;        /*  get flags of device.  */

            /*  check flags. */
            if ((*a & 2) != 0)
            {
                WpopUp(CNT_CNT,10,10,20,50,3,WHITE|_RED,WHITE|_RED);
                WputCen("\n\nField Instrument");
                Wputf("\n    at address %d", trial_address);
                WputCen("\nIndicates it requires EE burn");
                WputCen("\n\nCan't continue.");
                WputCen("\nGoing to next trial.");
                delay(1000); Wclose(); continue;
            }

            /*  get checksum. */
            j += 12;    /*  advance to checksum. */
            r = &receive[cs_start-1]; /*  point to start delim. */
            re = &receive_err[cs_start-1];
            if ((Checksum(r, j-cs_start+1)) != 0)
            {
                WpopUp(CNT_CNT,10,10,20,50,3,WHITE|_RED,WHITE|_RED);
                WputCen("\n\nField Instrument Response");
                Wputf("\n    at address %d", trial_address);
                WputCen("\nContains Bad Checksum.");
                WputCen("\n\nGoing to next trial.");
                delay(1000); Wclose(); continue;
            }

            if ((Checkpar(re, j-cs_start+1)) != 0)
            {
                WpopUp(CNT_CNT,10,10,20,50,3,WHITE|_RED,WHITE|_RED);
                WputCen("\n\nField Instrument Response");
                Wputf("\n    at address %d", trial_address);
                WputCen("\nContains Bad Parity.");
                WputCen("\nGoing to next trial.");
                delay(1000); Wclose(); continue;
            }

            /*  if we get here, then device found.  */
            addrs[trial_address] = 1;   /*  set flag.  */
            /*  save receive info.  */
            cmd0_rec_bytes[trial_address] = rec_bytes;
            for (k = 0; k<50; ++k)
            {
                cmd0_receive[trial_address][k] = receive[k];
            }
            break;  /*  stop trying this address.  */
        }
    }

    Wclose();   /*  Close address indication window.  */

    /*  Are there any field devices?  */
    for (i1 = 0,trial_address = 0; trial_address < 16; ++trial_address)
        i1 |= addrs[trial_address];
    if (i1 == 0)
    {
        Wclear();
        Wputs("\n\n    No Field Instrument Found");
        Wputs("\n\n    Hit any key to resume.");
        KeyWait(); Wclose(); return(0);
    }

    /*  Determine which device to use.  */
    while (1)
    {
        Wclear();
        Wputs("\n\n   Responses occurred at the\n");
        Wputs("   following Polling Addresses:\n\n    ");
        for (trial_address = 0; trial_address < 16; ++trial_address)
        {
            if (addrs[trial_address] == 1) Wputf("%d, ", trial_address);
        }
        Wputs("\n");
        KwGetFmt(ss, "'\n\n   Select an Address to Use: '!R!#!R!#");
        i1 = atoi(ss);
        if ((addrs[i1] == 0) || (i1 > 15))
        {
            /*  bad address. */
            Wclear();
            Wputs("\n\n     Bad address selected.\n");
            Wputs("\n\n     Hit any key to resume.");
            KeyWait();
        }
        else break;
    }

    polling_addr = (byte)i1;

    /*  re-parse the receive info for the selected polling address.  */

    /*  first get back receive data for this address.  */
    rec_bytes = cmd0_rec_bytes[polling_addr];
    for (i=0; i<50; ++i)
    {
        receive[i] = cmd0_receive[polling_addr][i];
    }

    k = rec_bytes/256;

    /* find start delimiter.  */
    r = receive;
    for (j=1; j <= k; ++j) if ((strncmp(r++, start_delim, 3)) == 0) break;

    /* go to start delim.  */
    j += 2; ++r;

    ++j; ++r;       /* advance to address. */

    /*  advance to command.  */
    ++j; ++r;

    ++j; ++r;   /*  advance to byte count.  */

    byte_count = *r;    /*  save byte count. */

    ++j;  ++r;  /*  skip to status. */
    status1 = *r++; ++j;     /*  save it.  */
    status2 = *r++; ++j;

    /*  now pointing at 1st byte of receive data. */
    a = addr;   /*  set pointer. */

    /*  finish the message. */

    r += 3;         /*  point to numb preambles. */
    *a++ = *r++;    /*  get numb preambles. */
    r -= 3;         /*  point to MFR ID. */
    *a++ = *r++;    /*  get MFR ID (1st byte of addr). */
    *a++ = *r++;    /*  get MFR DEVICE TYPE (2nd byte). */

    r += 6;     /*  point to DEVICE ID MSB (3rd addr byte).  */
    *a++ = *r++;    /*  get 3 bytes */
    *a++ = *r++;
    *a++ = *r++;

    r -= 6;         /*  back up to software rev of dev. */
    *a++ = *r++;    /*  get software rev of device. */
    *a++ = *r++;    /*  get hardware rev of device. */
    *a = *r;        /*  get flags of device.  */

    Wclose();
    WVprtf(status_wind, 0,62, BLACK|_GREEN, "     ");
    WVprtf(status_wind, 0,64, BLACK|_GREEN, "%d", polling_addr);
    found = 1; return(1);
}

